package net.wnb.mineclearance.model;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;

public class Game {
	public static final int DEFAULT_ROW_COUNT = 16;
	public static final int DEFAULT_COL_COUNT = 16;
	public static final int DEFAULT_MINE_COUNT = 16;
	public static final int STATE_PROCESSING = 0;
	public static final int STATE_SUCCESSED = 1;
	public static final int STATE_FAILED = 2;
	
	private int rowCount;
	private int colCount;
	private int mineCount;
	private boolean[][] map;
	private boolean[][] mask;
	private int state;//游戏状态
	private ArrayList<GameStateListener> listeners;
	private ArrayList<RunningTimeRefreshedListener> runningTimeRefreshedListeners;
	private Timer timer;
	private int runningTime;//运行时间
	
	public Game(int rowCount, int colCount, int mineCount){
		setRowCount(rowCount);
		setColCount(colCount);
		setMineCount(mineCount);
		listeners = new ArrayList<GameStateListener>();
		runningTimeRefreshedListeners = new ArrayList<RunningTimeRefreshedListener>();
		timer = new Timer();
		
		initData();
		initMask();
	}

	public Game(int rowCount, int colCount, boolean[] map, boolean[] mask,
			int runningTime) {
		setRowCount(rowCount);
		setColCount(colCount);
		setRunningTime(runningTime);
		
		this.map = new boolean[this.rowCount][this.colCount];
		for(int i=0;i<this.rowCount;i++){
			for(int j=0;j<this.colCount;j++){
				this.map[i][j] = map[i * this.colCount + j];
			}
		}
		
		this.mask = new boolean[this.rowCount][this.colCount];
		for(int i=0;i<this.rowCount;i++){
			for(int j=0;j<this.colCount;j++){
				this.mask[i][j] = mask[i * this.colCount + j];
			}
		}
		
		listeners = new ArrayList<GameStateListener>();
		runningTimeRefreshedListeners = new ArrayList<RunningTimeRefreshedListener>();
		timer = new Timer();
	}

	private void setRunningTime(int runningTime) {
		this.runningTime = runningTime > 0 ? runningTime : 0;
	}

	private void initMask() {
		mask = new boolean[rowCount][colCount];
	}

	private void initData() {
		map = new boolean[rowCount][colCount];
		Random rand = new Random();
		
		for(int i=0;i<mineCount;i++){
			int rowIndex = rand.nextInt(rowCount);
			int colIndex = rand.nextInt(colCount);
			if(map[rowIndex][colIndex] == true){
				i--;
				continue;
			}
			
			map[rowIndex][colIndex] = true;
		}
	}

	private void setMineCount(int mineCount) {
		this.mineCount = mineCount > 0 ? mineCount : DEFAULT_MINE_COUNT;
	}

	private void setColCount(int colCount) {
		this.colCount = colCount > 0 ? colCount : DEFAULT_COL_COUNT;
	}

	private void setRowCount(int rowCount) {
		this.rowCount = rowCount > 0 ? rowCount : DEFAULT_ROW_COUNT;
	}

	public int getRowCount() {
		return rowCount;
	}

	public int getColCount() {
		return colCount;
	}

	public boolean getMaskAt(int i, int j) {
		if(withinRange(i, j)){
			return mask[i][j];
		}
		
		throw new ArrayIndexOutOfBoundsException();
	}

	public int calcCount(int i, int j) {
		if(withinRange(i, j)){
			if(map[i][j] == true)
				return -1;
			
			int startY = Math.max(0, i-1);
			int startX = Math.max(0, j-1);
			int endY = Math.min(i+1, rowCount-1);
			int endX = Math.min(j+1, colCount-1);
			int sum = 0;
			
			for(int m = startY;m<=endY;m++){
				for(int n = startX;n<=endX;n++){
					if(map[m][n] == true){
						sum++;
					}
				}
			}
			
			return sum;
		}
		
		throw new ArrayIndexOutOfBoundsException();
	}

	private boolean withinRange(int i, int j) {
		return i >= 0 && i < rowCount && j >= 0 && j < colCount;
	}

	public void open(int rowIndex, int colIndex) {
		if(withinRange(rowIndex, colIndex)){
			openGrids(rowIndex, colIndex);
			if(map[rowIndex][colIndex] == true){
				fail();
				notifyGameStateListener();
			}else{
				//计算打开的数量是否等于非地雷的数量
				int sum = 0;
				for(int i=0;i<rowCount;i++){
					for(int j=0;j<colCount;j++){
						if(mask[i][j] == true && map[i][j] == false){
							sum++;
						}
					}
				}
				
				if(sum == rowCount * colCount - mineCount){
					state = STATE_SUCCESSED;
					notifyGameStateListener();
					end();
				}
			}
		}
	}
	
	private void openGrids(int rowIndex, int colIndex) {
		if(mask[rowIndex][colIndex] == true)
			return;
		
		mask[rowIndex][colIndex] = true;
		if(calcCount(rowIndex, colIndex) != 0)
			return;
		
		int startX = Math.max(colIndex -1, 0);
		int endX = Math.min(colIndex+1, colCount -1);
		int startY = Math.max(rowIndex-1, 0);
		int endY = Math.min(rowIndex+1, rowCount -1);
		for(int i=startY;i<=endY;i++){
			for(int j=startX;j<=endX;j++){
				openGrids(i, j);
			}
		}
	}

	private void fail() {
		state = STATE_FAILED;
		for(int i=0;i<rowCount;i++){
			for(int j=0;j<colCount;j++){
				if(map[i][j] == true){
					mask[i][j] = true;
				}
			}
		}
		
		end();
	}
	
	public void end(){
		/*timer.schedule(new TimerTask(){

			@Override
			public void run() {
				MainWindow win = MainWindow.getInstance();
				win.setPane(new SelectionPane());
				dispose();
			}}, 1875);*/
		timer.cancel();
	}
	
	public void dispose(){
		timer.cancel();
	}

	public void addGameStateListener(GameStateListener listener){
		listeners.add(listener);
	}

	private void notifyGameStateListener() {
		Iterator<GameStateListener> iterator = listeners.iterator();
		while(iterator.hasNext()){
			GameStateListener listener =  iterator.next();
			listener.onStateChanged(state);
		}
	}

	public int getState() {
		return state;
	}

	public void start() {
		state = STATE_PROCESSING;
		timer.scheduleAtFixedRate(new TimerTask(){
			@Override
			public void run(){
				runningTime++;
				
				//通知监听器
				Iterator<RunningTimeRefreshedListener> iterator = runningTimeRefreshedListeners.iterator();
				while(iterator.hasNext()){
					RunningTimeRefreshedListener listener = iterator.next();
					listener.onRunningTimeRefreshed(runningTime);
				}
			}
		}, 0, 1000);
	}

	public int getRunningTime(){
		return runningTime;
	}
	
	public void addRunningTimeRefreshedListener(RunningTimeRefreshedListener listener){
		runningTimeRefreshedListeners.add(listener);
	}

	public int getMineCount() {
		return mineCount;
	}

	public boolean getValueAt(int i, int j) {
		if(withinRange(i, j)){
			return map[i][j];
		}
		
		throw new ArrayIndexOutOfBoundsException();
	}
}
